package com.xinchao.job.service.advertise;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.xinchao.common.constant.BusinessConstant;
import com.xinchao.common.model.advertise.*;
import com.xinchao.common.util.ListUtil;
import com.xinchao.job.mapper.dmp.ParamLogMapper;
import com.xinchao.job.mapper.dmp.PremisesMapper;
import com.xinchao.job.mapper.dmp.ProgramInfoMapper;
import com.xinchao.job.mapper.dmp.RouteMapper;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @ClassName AdvertiseService
 * @Description TOOD
 * @Author HanQinrui
 * @Date 2018/9/28 16:01
 * @Version 1.0
 **/
@Service("advertiseService")
public class AdvertiseService {
    private static Logger logger = LoggerFactory.getLogger(AdvertiseService.class);
    @Autowired
    private ParamLogMapper paramLogMapper;
    @Autowired
    private RouteMapper routeMapper;
    @Autowired
    private ProgramInfoMapper programInfoMapper;
    @Autowired
    private PremisesMapper premisesMapper;

    @Autowired
    private TransactionTemplate transactionTemplate;


    /**
     * 处理播控助手参数日志详情表中 还未执行的数据
     * 执行流程：根据城市、数据类型分组，取出最新上刊时间、最新添加时间且暂未执行的数据，清空城市里对应数据类型相关的表，
     * 从阿里云上下载资源到本地，解析资源，将数据依次存进数据库中。
     * 以保证当前库中只保留了某一城市最新的数据。
     */
    public void handleAdvertiseInfo() throws Exception {
        // 获取暂未执行的数据
        List<ParamLog> paramLogList = paramLogMapper.getParamLogListByStatus(BusinessConstant.PARAM_DATA_NOT_STARTED);
        for (ParamLog paramLog : paramLogList) {
            // 操作时间
            Long currentTime = System.currentTimeMillis();
            // 城市编码
            String cityCode = paramLog.getCityCode();
            // 数据状态设置为 2执行中
            paramLog.setStatus(BusinessConstant.PARAM_DATA_EXECUTING);

            //判断是否争抢到资源
            boolean isContinue = transactionTemplate.execute(new TransactionCallback<Boolean>() {
                @Override
                public Boolean doInTransaction(TransactionStatus status) {
                    try {
                        return paramLogMapper.updateParamLog(paramLog) > 0;
                    } catch (Exception e) {
                        status.setRollbackOnly();
                    }
                    return false;
                }
            });

            if (!isContinue) {
                System.out.println("执行失败,未抢占到资源");
                continue;
            }

            boolean isSuccess = transactionTemplate.execute(new TransactionCallback<Boolean>() {
                @Override
                public Boolean doInTransaction(TransactionStatus status) {
                    try {
                        // 数据类型
                        String dataType = String.valueOf(paramLog.getDataType());
                        switch (dataType) {
                            case BusinessConstant.LUXIAN_TYPE:
                                // 路线表
                                analyzeRoute(cityCode, dataType, paramLog.getDataUrl(), currentTime);
                                break;
                            case BusinessConstant.SHANGKAN_TYPE:
                                // 上刊表
                                analyzePrograms(cityCode, dataType, paramLog.getDataUrl(), currentTime);
                                break;
                            case BusinessConstant.LUDAN_TYPE:
                                // 路单表
                                analyzePremises(cityCode, dataType, paramLog.getDataUrl(), currentTime);
                                break;
                            default:
                                throw new RuntimeException();
                        }
                        // 修改paramLog
                        paramLog.setModifyTime(currentTime);
                        // 设置数据状态为1已完成
                        paramLog.setStatus(BusinessConstant.PARAM_DATA_COMPLETED);
                        paramLogMapper.updateParamLogStatus(paramLog);

                        // 将当前城市、当前数据类型、当前上刊时间之后的数据状态改为未执行
                        paramLogMapper.updateParamLogNotStarted(paramLog);
                    } catch (Exception e) {
                        status.setRollbackOnly();
                        System.out.println("执行失败," + e.getMessage());
                        return false;
                    }
                    return true;
                }
            });

            //如果处理失败的话，更新param_log状态为error,进行人工干预
            if (!isSuccess) {
                transactionTemplate.execute(new TransactionCallback() {
                    @Override
                    public Object doInTransaction(TransactionStatus status) {
                        paramLog.setStatus(BusinessConstant.PARAM_DATA_ERROR);
                        paramLogMapper.updateParamLogStatus(paramLog);
                        return null;
                    }
                });
            }
        }
    }

    /**
     * 下载JSON文件
     *
     * @param cityCode    城市编码
     * @param type        方法类型
     * @param downloadURL 下载JSON文件地址
     */
    public String downloadJSONObject(String cityCode, String type, String downloadURL) {
        FileOutputStream fos = null;
        try {
            if (StringUtils.isBlank(downloadURL) || StringUtils.isBlank(type)) {
                throw new NullPointerException("");
            }
            String typeName = "";
            String fileName = "";
            switch (type) {
                case BusinessConstant.LUXIAN_TYPE:
                    typeName = "luxian";
                    fileName = "路线表.json";
                    break;
                case BusinessConstant.SHANGKAN_TYPE:
                    typeName = "shangkan";
                    fileName = "上刊表.json";
                    break;
                case BusinessConstant.LUDAN_TYPE:
                    typeName = "ludan";
                    fileName = "路单表.json";
                    break;
                default:
            }
            File tempFile = new File("advertise/" + cityCode + "/" + typeName + "/" + fileName);
            //如果文件父目录不存在，就创建文件父目录
            if (!tempFile.getParentFile().exists()) {
                tempFile.getParentFile().mkdirs();
            }
            URL url = new URL(downloadURL);
            ReadableByteChannel rbc = Channels.newChannel(url.openStream());
            fos = new FileOutputStream(tempFile, false);
            fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
            logger.info("###@downloadJSONObject 下载成功，保存路径为: " + tempFile.getPath());
            return tempFile.getPath();
        } catch (IOException e) {
            e.printStackTrace();
            logger.error("###@downloadJSONObject 下载失败: " + e.toString() + ",URL: " + downloadURL);
            return null;
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 解析路线表信息
     *
     * @param cityCode
     * @param dataTpe
     * @param downloadUrl
     * @param currentTime
     * @throws Exception
     */
    public void analyzeRoute(String cityCode, String dataTpe, String downloadUrl, Long currentTime) throws Exception {

        // 下载路线表
        String luxianPath = downloadJSONObject(cityCode, dataTpe, downloadUrl);
        String routeData = FileUtils.readFileToString(new File(luxianPath), "UTF-8");
        JSONArray jsonArray = JSON.parseArray(routeData);
        // 解析出路线List
        List<Route> routeList = Lists.newArrayList();
        for (int i = 0; i < jsonArray.size(); i++) {
            Route route = new Route();
            JSONObject jsonObject = jsonArray.getJSONObject(i);
            route.setVersion(jsonObject.getLong("id"));
            route.setName(jsonObject.getString("name"));
            routeList.add(route);
        }

        // 将该城市下的路线添加进历史表
        routeMapper.insertHisRoute(cityCode, currentTime);
        // 删除该城市下的路线
        routeMapper.deleteRouteByCityCode(cityCode);
        // 保存该城市下的路线
        routeMapper.insertRoute(routeList, cityCode, currentTime);
    }

    /**
     * 解析上刊表信息
     *
     * @param cityCode
     * @param dataType
     * @param downloadUrl
     * @param currentTime
     * @throws Exception
     */
    public void analyzePrograms(String cityCode, String dataType, String downloadUrl, Long currentTime) throws Exception {
        // 将该城市的上屏节目素材列表添加进历史表
        programInfoMapper.insertHisProgramTop(cityCode, currentTime);
        // 删除上屏节目素材列表
        programInfoMapper.deleteProgramTopByCityCode(cityCode);

        // 将该城市的下屏节目素材列表添加进历史表
        programInfoMapper.insertHisProgramBottom(cityCode, currentTime);
        // 删除下屏节目素材列表
        programInfoMapper.deleteProgramBottomByCityCode(cityCode);

        // 将该城市的节目列表添加进历史表
        programInfoMapper.insertHisProgramInfo(cityCode, currentTime);
        // 删除节目列表
        programInfoMapper.deleteProgramInfoByCityCode(cityCode);

        // 下载上刊表
        String shangkanPath = downloadJSONObject(cityCode, dataType, downloadUrl);
        String shangkanData = FileUtils.readFileToString(new File(shangkanPath), "UTF-8");
        JSONObject shangkanJsonObject = JSON.parseObject(shangkanData);
        // 解析topOrders
        String topOrders = shangkanJsonObject.getString("topOrders");
        List<ProgramTop> programTopList = JSONObject.parseArray(topOrders, ProgramTop.class);
        programInfoMapper.insertProgramTop(programTopList, cityCode, currentTime);
        // 解析bottomOrders
        String bottomOrders = shangkanJsonObject.getString("bottomOrders");
        List<ProgramBottom> programBottomsList = JSONObject.parseArray(bottomOrders, ProgramBottom.class);
        programInfoMapper.insertProgramBottom(programBottomsList, cityCode, currentTime);
        // 解析orders
        String orders = shangkanJsonObject.getString("orders");
        List<ProgramInfo> programInfoList = JSONObject.parseArray(orders, ProgramInfo.class);
        // 每2000条数据处理
        List<List<ProgramInfo>> singleList = ListUtil.singleLimitAssign(programInfoList, 2000);
        for (List<ProgramInfo> list : singleList) {
            programInfoMapper.insertProgramInfo(list, cityCode, currentTime);
        }
    }

    /**
     * 解析路单表信息
     * @param cityCode
     * @param dataType
     * @param downloadUrl
     * @param currentTime
     * @throws Exception
     */
    public void analyzePremises(String cityCode, String dataType, String downloadUrl, Long currentTime) throws Exception {
        // 将该城市的上刊下刊素材列表写进历史表
        routeMapper.insertHisSkXkOrders(cityCode, currentTime);
        // 删除上刊下刊素材列表
        routeMapper.deleteSkXkOrdersByCityCode(cityCode);

        // 将该城市的项目列表写进历史表
        premisesMapper.insertHisPremises(cityCode, currentTime);
        // 删除项目列表
        premisesMapper.deletePremisesByCityCode(cityCode);

        // 将该城市下的路线项目关系写进历史表
        premisesMapper.insertHisRoutePremises(cityCode, currentTime);
        // 删除路线项目关系
        premisesMapper.deleteRoutePremisesByCityCode(cityCode);

        // 将该城市下的路单终端写进历史表
        premisesMapper.insertHisTravelDevice(cityCode, currentTime);
        // 删除路单终端
        premisesMapper.deleteTravelDeviceByCityCode(cityCode);

        // 将该城市下的路单终端关系写进历史表
        premisesMapper.insertHisPremisesTravelDevice(cityCode, currentTime);
        // 删除项目路单终端关系
        premisesMapper.deletePremisesTravelDeviceByCityCode(cityCode);

        // 将该城市下的项目节目关系写进历史表
        premisesMapper.insertHisPremisesProgram(cityCode, currentTime);
        // 删除项目和节目关系
        premisesMapper.deletePremisesProgramByCityCode(cityCode);

        // 下载路单表
        String ludanPath = downloadJSONObject(cityCode, dataType, downloadUrl);
        Long systime = System.currentTimeMillis();
        String ludanData = FileUtils.readFileToString(new File(ludanPath), "UTF-8");
        JSONObject jsonObject = JSON.parseObject(ludanData);

        // 解析skXkOrders
        String skOrders = jsonObject.getString("skOrders");
        String xkOrders = jsonObject.getString("xkOrders");
        SkXkOrders skXkOrders = new SkXkOrders();
        skXkOrders.setSkOrders(skOrders);
        skXkOrders.setXkOrders(xkOrders);
        skXkOrders.setCityCode(cityCode);
        skXkOrders.setAddTime(systime);
        routeMapper.insertSkXkOrders(skXkOrders);

        // 解析orders
        JSONArray ordersArray = jsonObject.getJSONArray("orders");
        String ordersStr = JSONObject.toJSONString(ordersArray);
        List<RouteDTO> routeList = JSONObject.parseArray(ordersStr, RouteDTO.class);
        // 批量更新route信息
        routeMapper.updateBatchRouteByCityCode(routeList, cityCode);

        for (RouteDTO routeDTO : routeList) {
            // 批量插入项目premises
            String routeListStr = routeDTO.getRouteList();
            List<PremisesDTO> premisesList = JSONObject.parseArray(routeListStr, PremisesDTO.class);
            for(PremisesDTO dto : premisesList) {
                dto.setCityCode(cityCode);
                dto.setAddTime(systime);
            }
            premisesMapper.insertBatchPremises(premisesList);

            // 保存route和premises的关系
            List<RoutePremises> routePremisesList = premisesList.stream().map(item -> {
                RoutePremises bean = new RoutePremises();
                bean.setRouteId(Long.valueOf(routeDTO.getRouteName()));
                bean.setPremisesId(item.getPremisesId());
                bean.setCityCode(cityCode);
                bean.setAddTime(systime);
                return bean;
            }).collect(Collectors.toList());
            premisesMapper.insertBatchRoutePremises(routePremisesList);

            for (PremisesDTO premisesDTO : premisesList) {
                // 解析orderList
                String orderListStr = premisesDTO.getOrderList();
                List<TravelDeviceDTO> travelDeviceList = JSONObject.parseArray(orderListStr, TravelDeviceDTO.class);
                for (TravelDeviceDTO dto : travelDeviceList) {
                    dto.setCityCode(cityCode);
                    dto.setAddTime(systime);
                }
                // 保存机器列表
                premisesMapper.insertBatchTravelDevice(travelDeviceList);

                // 保存premises和travelDevice的关系
                List<PremisesTravelDevice> premisesTravelDeviceList = travelDeviceList.stream().map(item -> {
                    PremisesTravelDevice bean = new PremisesTravelDevice();
                    bean.setTravelDeviceId(item.getTravelDeviceId());
                    bean.setPremisesId(premisesDTO.getPremisesId());
                    bean.setCityCode(cityCode);
                    bean.setAddTime(systime);
                    return bean;
                }).collect(Collectors.toList());
                premisesMapper.insertBatchPremisesTravelDevice(premisesTravelDeviceList);

                // 保存premises和program的关系
                // 根据机器列表查询节目列表
                List<ProgramInfo> programIdList = programInfoMapper.getProgramByTravelDeviceList(travelDeviceList);
                List<PremisesProgram> premisesProgramsList = programIdList.stream().map(item -> {
                    PremisesProgram bean = new PremisesProgram();
                    bean.setPremisesId(premisesDTO.getPremisesId());
                    bean.setProgramId(item.getProgramId());
                    bean.setCityCode(cityCode);
                    bean.setAddTime(systime);
                    return bean;
                }).collect(Collectors.toList());
                premisesMapper.insertBatchPremisesProgram(premisesProgramsList);
            }
        }
    }

}
